package com.duobaoyu.dby.supply.biz.client.boundary.refund;

import cn.hutool.core.math.Money;
import com.alibaba.fastjson.JSON;
import com.duobaoyu.dby.channel.adapter.bean.exception.ExceptionResult;
import com.duobaoyu.dby.channel.adapter.bean.refund.WaybillInfo;
import com.duobaoyu.dby.channel.adapter.enums.ChannelCodeEnum;
import com.duobaoyu.dby.channel.adapter.enums.biz.order.OrderStatusEnum;
import com.duobaoyu.dby.channel.adapter.enums.biz.product.DeliveryWayEnum;
import com.duobaoyu.dby.channel.adapter.enums.biz.product.ProductTypeEnum;
import com.duobaoyu.dby.channel.adapter.enums.biz.refund.AfterSaleTypeEnum;
import com.duobaoyu.dby.channel.adapter.enums.biz.refund.PickupProductEnum;
import com.duobaoyu.dby.channel.adapter.util.ChannelCodeUtils;
import com.duobaoyu.dby.channel.boundary.bean.area.AddressInfoParam;
import com.duobaoyu.dby.channel.boundary.bean.order.param.OrderCancelParam;
import com.duobaoyu.dby.channel.boundary.bean.order.result.OrderCancelResult;
import com.duobaoyu.dby.channel.boundary.bean.refund.param.*;
import com.duobaoyu.dby.channel.boundary.bean.refund.result.*;
import com.duobaoyu.dby.channel.boundary.template.SupplyChannelTemplate;
import com.duobaoyu.dby.common.core.constant.CommonConstants;
import com.duobaoyu.dby.common.core.constant.StringPool;
import com.duobaoyu.dby.common.core.util.DateUtils;
import com.duobaoyu.dby.supply.biz.client.boundary.order.bean.bo.OrderBO;
import com.duobaoyu.dby.supply.biz.client.boundary.order.bean.bo.OrderProductBO;
import com.duobaoyu.dby.supply.biz.client.boundary.refund.bo.AfterSaleLogisticsBO;
import com.duobaoyu.dby.supply.biz.exception.SupplyChainBizException;
import com.duobaoyu.dby.supply.common.biz.bo.refund.*;
import com.duobaoyu.dby.supply.common.biz.bo.refund.client.AfterSaleApplyClientOutBO;
import com.duobaoyu.dby.supply.core.common.exception.CustomAssert;
import com.google.common.collect.Lists;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

import static com.duobaoyu.dby.common.core.constant.CommonConstants.HUNDRED;
import static com.duobaoyu.dby.common.core.constant.CommonConstants.ZERO;

/**
 * 售后聚合策略
 *
 * @author fengshaoyu
 * @version 1.0.0
 * @date 2023/9/26 9:30
 */
@Slf4j
//@Primary
@Service
@RequiredArgsConstructor
public class AfterSaleBoundaryClientImpl implements AfterSaleBoundaryClient {

    private final SupplyChannelTemplate supplyChannelTemplate;

    @Override
    public AfterSaleLogisticsBO queryAfterSaleLogistics(String channelCode,
                                                        String afterSaleSn, String channelOrderSn) {
        AfterSaleLogisticsParam afterSaleLogisticsParam = new AfterSaleLogisticsParam();
        afterSaleLogisticsParam.setChannelCode(channelCode);
        afterSaleLogisticsParam.setAfterSaleTradeSn(afterSaleSn);
        afterSaleLogisticsParam.setChannelOrderSn(channelOrderSn);
        AfterSaleLogisticsResult afterSaleLogisticsResult = supplyChannelTemplate.afterSaleLogistics(afterSaleLogisticsParam);
        return AfterSaleLogisticsBO.builder()
                .afterSaleApplySn(afterSaleLogisticsResult.getAfterSaleTradeSn())
                .channelOrderSn(afterSaleLogisticsResult.getChannelOrderSn())
                .logistics(afterSaleLogisticsResult.getLogistics().stream()
                        .map(item -> AfterSaleLogisticsBO.Logistics.builder()
                                .channelExpressCompanyName(item.getChannelExpressCompanyName())
                                .channelExpressCompanyCode(item.getChannelExpressCompanyCode())
                                .build())
                        .collect(Collectors.toList()))
                .build();
    }

    @Override
    public RefundCancelResult cancelOrder(AfterSaleOutBO afterSale, String cancelRemark) {
        RefundCancelParam refundCancelParam = new RefundCancelParam();
        refundCancelParam.setChannelCode(afterSale.getChannelCode());
        refundCancelParam.setChannelOrderSn(afterSale.getChannelOrderSn());
        refundCancelParam.setAfterSaleTradeSn(afterSale.getAfterSaleTradeOrderSn());
        refundCancelParam.setCancelAfterSaleReason(cancelRemark);

        try {
            return supplyChannelTemplate.cancelAfterSale(refundCancelParam);
        } catch (UnsupportedOperationException e) {
            return null;
        }
    }

    @Override
    public AfterSaleConfirmResult confirmAfterSale(AfterSaleOutBO afterSale) {
        AfterSaleConfirmParam param = new AfterSaleConfirmParam();
        param.setChannelCode(afterSale.getChannelCode());
        param.setAfterSaleApplySnList(Lists.newArrayList(afterSale.getAfterSaleSn()));
        param.setChannelOrderSn(afterSale.getChannelOrderSn());
        param.setChannelAfterSaleSn(afterSale.getChannelAfterSaleSn());
        try {
            return supplyChannelTemplate.afterSaleConfirm(param);
        } catch (UnsupportedOperationException e) {
            return null;
        }
    }

    @Override
    public AfterSaleRefundDetailResult jdVopAfterSaleRefundDetail(AfterSaleOutBO afterSale) {
        AfterSaleRefundDetailParam param = new AfterSaleRefundDetailParam();
        param.setChannelCode(ChannelCodeEnum.JD_VOP.getCode());
        param.setChannelAfterSaleSn(afterSale.getChannelAfterSaleSn());
        param.setChannelOrderSn(afterSale.getChannelOrderSn());
        return supplyChannelTemplate.afterSaleRefundDetail(param);
    }

    @Override
    public List<AfterSaleAttributesResult> queryAfterSaleReason(OrderBO order, List<OrderProductBO> orderProducts) {
        AfterSaleAttributesParam param = new AfterSaleAttributesParam();
        param.setChannelCode(order.getChannelCode());
        param.setChannelTradeOrderSn(order.getChannelTradeOrderSn());
        param.setChannelOrderSn(order.getChannelOrderSn());
        param.setChannelPreOrderSn(order.getChannelOrderParentSn());
        param.setOrderTradeSn(order.getOrderTradeSn());
        param.setOrderStatus(order.getOrderStatus());
        param.setSkuList(orderProducts.stream().map(op -> {
            AfterSaleAttributesParam.Sku sku = new AfterSaleAttributesParam.Sku();
            sku.setChannelSkuCode(op.getChannelSkuCode());
            sku.setSkuCode(op.getSkuCode());
            sku.setRepositoryType(op.getRepositoryType());
            sku.setOrderQuantity(op.getSkuNum());
            sku.setAfterSaleQuantity(CommonConstants.ZERO);
            sku.setCloseQuantity(Optional.ofNullable(op.getChannelReturnSkuNum()).orElse(ZERO));
            return sku;
        }).collect(Collectors.toList()));
        return supplyChannelTemplate.afterSaleAttributesQuery(Lists.newArrayList(param));
    }


    @Override
    public List<AfterSaleReturnAddressResult> afterSaleAddressQuery(List<AfterSaleOutBO> afterSales) {
        List<AfterSaleReturnAddressParam> params = afterSales.stream().map(afterSale -> {
            AfterSaleReturnAddressParam param = new AfterSaleReturnAddressParam();
            param.setAfterSaleSn(afterSale.getAfterSaleSn());
            param.setChannelCode(afterSale.getChannelCode());
            param.setAfterSaleTradeSn(afterSale.getAfterSaleTradeOrderSn());
            param.setChannelOrderSn(afterSale.getChannelOrderSn());
            param.setChannelAfterSaleSn(afterSale.getChannelAfterSaleSn());
            return param;
        }).collect(Collectors.toList());
        List<AfterSaleReturnAddressResult> addressResults =
                supplyChannelTemplate.afterSaleAddressQuery(params);
        CustomAssert.isTrue(addressResults.stream().anyMatch(ExceptionResult::getSuccess), "查询售后寄回地址失败！");
        return addressResults;
    }

    @Override
    public List<AfterSaleAttributesResult> afterSaleAttributesQuery(List<AfterSaleAttributesParam> params) {
        return supplyChannelTemplate.afterSaleAttributesQuery(params);
    }

    @Override
    public AfterSaleWayBillResult backFillWaybill(AfterSaleOutBO item, AfterSaleWayBillInBO
            waybill) {
        AfterSaleWayBillParam param = new AfterSaleWayBillParam();
        param.setChannelCode(item.getChannelCode());
        param.setAfterSaleTradeSn(item.getAfterSaleTradeOrderSn());
        param.setChannelOrderSn(item.getChannelOrderSn());
        WaybillInfo waybillInfo = new WaybillInfo();
        waybillInfo.setDeliverDate(waybill.getDeliverDate().format(DateUtils.DEFAULT_DATE_TIME_FORMATTER));
        waybillInfo.setProductNum(waybill.getSendingBackQuantity());
        waybillInfo.setExpressCode(waybill.getExpressCode());
        waybillInfo.setChannelSkuCode(waybill.getChannelSkuCode());
        waybillInfo.setProductType(ProductTypeEnum.getProductTypeEnum(waybill.getProductType()));
        waybillInfo.setChannelExpressCompanyCode(waybill.getExpressCompanyCode());
        waybillInfo.setChannelExpressCompany(waybill.getExpressCompany());
        waybillInfo.setFreightMoney(new Money(waybill.getFreightAmount()).getAmount());
        waybillInfo.setVouchers(Lists.newArrayList());
        param.setWaybillInfoList(Lists.newArrayList(waybillInfo));
        param.setChannelAfterSaleSn(item.getChannelAfterSaleSn());
        param.setAfterSalesType(item.getAfterSaleType());
        return supplyChannelTemplate.afterSaleWayBill(param);
    }

    @Override
    public AfterSaleVoucherResult afterSaleVoucher(AfterSaleVoucherParam param) {
        return supplyChannelTemplate.afterSaleVoucher(param);
    }

    @Override
    public AfterSaleParticularResult queryAfterSale(AfterSaleOutBO afterSale) {
        AfterSaleParticularParam param = new AfterSaleParticularParam();
        param.setChannelCode(afterSale.getChannelCode());
        param.setAfterSaleTradeSn(afterSale.getAfterSaleTradeOrderSn());
        param.setChannelOrderSn(afterSale.getChannelOrderSn());
        param.setChannelAfterSaleSn(afterSale.getChannelAfterSaleSn());
        return supplyChannelTemplate.afterSaleParticular(param);
    }

    @Override
    public AfterSaleApplyClientOutBO cancelOrder(AfterSaleOutBO afterSale) {
        CustomAssert.notNull(afterSale, "售后数据参数缺失！");

        AfterSaleApplyClientOutBO ret = null;

        ChannelCodeEnum channelCode = ChannelCodeEnum.getChannelCodeEnumByCode(afterSale.getChannelCode());
        try {
            if(ChannelCodeUtils.supportConfirmOrderAfterOrderCancel().contains(channelCode)) {
                OrderCancelParam param = new OrderCancelParam();
                param.setOrderTradeSn(afterSale.getOrderTradeSn());
                param.setChannelTradeOrderSn(afterSale.getChannelTradeOrderSn());
                param.setChannelCode(afterSale.getChannelCode());
                param.setOrderStatus(OrderStatusEnum.WAIT_SHIP.getValue());
                if(log.isDebugEnabled()) {
                    log.debug("invoke channel cancel order,param = {}", JSON.toJSONString(param));
                }
                OrderCancelResult orderCancelResult = supplyChannelTemplate.cancelOrder(param);
                ret = new AfterSaleApplyClientOutBO();
                ret.setChannelAfterSaleTradeSn(afterSale.getAfterSaleTradeOrderSn());
                ret.setChannelAfterSaleSn(afterSale.getAfterSaleTradeOrderSn());
                ret.setApplyResult(orderCancelResult.getSuccess());
                ret.setErrorMessage(orderCancelResult.getErrorMessage());
            } else if(ChannelCodeUtils.supportConfirmOrderAfterOrderRefundAfterSale().contains(channelCode)) {
                AfterSaleApplyParam param = convertAfterSaleApplyParam(afterSale, channelCode, null, null);
                if(log.isDebugEnabled()) {
                    log.debug("invoke channel submit apply afterSale,param = {}", JSON.toJSONString(param));
                }
                AfterSaleApplyResult result = supplyChannelTemplate.applyAfterSale(param);
                ret = new AfterSaleApplyClientOutBO();
                ret.setChannelAfterSaleTradeSn(result.getAfterSaleTradeSn());
                ret.setChannelAfterSaleSn(result.getChannelAfterSaleSn());
                ret.setApplyResult(true);
            } else {
                log.warn("售后数据无法处理,param={}", JSON.toJSONString(afterSale));
                SupplyChainBizException.fail("售后数据无法处理(渠道不支持确认订单后取消)");
            }
        } catch (Exception e) {
            ret = new AfterSaleApplyClientOutBO();
            ret.setChannelAfterSaleTradeSn(afterSale.getAfterSaleTradeOrderSn());
            ret.setChannelAfterSaleSn(afterSale.getChannelAfterSaleSn());
            ret.setApplyResult(false);
            ret.setErrorMessage(StringUtils.substring(e.getMessage(), ZERO, HUNDRED));
        }
        return ret;
    }

    @Override
    public AfterSaleApplyClientOutBO applyAfterSale(AfterSaleOutBO afterSale,
                                                    AfterSalePickUpBO afterSalePickUp,
                                                    AfterSaleReturnShipmentBO returnShipment) {
        AfterSaleApplyClientOutBO ret;
        AfterSaleApplyParam param = null;
        ChannelCodeEnum channelCode = ChannelCodeEnum.getChannelCodeEnumByCode(afterSale.getChannelCode());
        try {
            param = convertAfterSaleApplyParam(afterSale, channelCode, afterSalePickUp, returnShipment);
            AfterSaleApplyResult result = supplyChannelTemplate.applyAfterSale(param);
            ret = new AfterSaleApplyClientOutBO();
            ret.setChannelAfterSaleTradeSn(result.getAfterSaleTradeSn());
            ret.setChannelAfterSaleSn(result.getChannelAfterSaleSn());
            ret.setApplyResult(true);
        } catch (Exception e) {
            log.warn("apply chanel afterSale throw exception,param={}", JSON.toJSONString(param), e);
            ret = new AfterSaleApplyClientOutBO();
            ret.setChannelAfterSaleTradeSn(afterSale.getAfterSaleTradeOrderSn());
            ret.setChannelAfterSaleSn(afterSale.getChannelAfterSaleSn());
            ret.setApplyResult(false);
            ret.setErrorMessage(StringUtils.substring(e.getMessage(), ZERO, HUNDRED));
        }
        return ret;
    }


    private AfterSaleApplyParam convertAfterSaleApplyParam(AfterSaleOutBO afterSale,
                                                           ChannelCodeEnum channelCode,
                                                           AfterSalePickUpBO afterSalePickUp,
                                                           AfterSaleReturnShipmentBO returnShipment) {
        AfterSaleApplyParam afterSaleApplyParam = new AfterSaleApplyParam();
        afterSaleApplyParam.setChannelCode(channelCode.getValue());
        afterSaleApplyParam.setAfterSaleTradeSn(afterSale.getAfterSaleTradeOrderSn());
        afterSaleApplyParam.setApplyItems(convertApplyItems(afterSale, afterSale.getItems()));
        afterSaleApplyParam.setCustomerInfo(convertCustomerInfo(afterSale.getContactInfo()));
        afterSaleApplyParam.setPickupInfo(convertPickUp(afterSalePickUp));
        afterSaleApplyParam.setReturnInfo(convertReturnShipment(returnShipment));
        return afterSaleApplyParam;
    }


    private List<AfterSaleApplyParam.AfterSaleApplyInfo> convertApplyItems(AfterSaleOutBO afterSale,
                                                                           List<AfterSaleItemOutBO> afterSales) {
        return afterSales.stream().map(e -> {
            AfterSaleApplyParam.AfterSaleApplyInfo afterSaleApplyInfo = new AfterSaleApplyParam.AfterSaleApplyInfo();
            afterSaleApplyInfo.setAfterSaleType(AfterSaleTypeEnum.getAfterSaleTypeEnum(e.getAfterSaleType()));
            afterSaleApplyInfo.setAfterSaleInfoDesc(convertAfterSaleInfoDesc(e));
            afterSaleApplyInfo.setAfterSaleSku(convertAfterSaleSku(afterSale, e));
            return afterSaleApplyInfo;
        }).collect(Collectors.toList());
    }

    private static AfterSaleApplyParam.AfterSaleInfoDesc convertAfterSaleInfoDesc(AfterSaleItemOutBO afterSale) {
        AfterSaleApplyParam.AfterSaleInfoDesc desc = new AfterSaleApplyParam.AfterSaleInfoDesc();
        desc.setQuestionDesc(afterSale.getAfterSaleRemark());
        if (CollectionUtils.isNotEmpty(afterSale.getAfterSaleImageList())) {
            desc.setQuestionPic(String.join(StringPool.COMMA, afterSale.getAfterSaleImageList()));
            desc.setVouchers(afterSale.getAfterSaleImageList());
        }
        return desc;
    }

    private AfterSaleApplyParam.CustomerInfo convertCustomerInfo(AfterSaleContactInfoBO customerInfo) {
        if (Objects.isNull(customerInfo)) {
            return null;
        }
        AfterSaleApplyParam.CustomerInfo ret = new AfterSaleApplyParam.CustomerInfo();
        ret.setCustomerName(customerInfo.getCustomerContactName());
        ret.setCustomerPhone(customerInfo.getCustomerMobilePhone());
        ret.setCustomerEmail(customerInfo.getCustomerEmail());
        ret.setCustomerPostcode(customerInfo.getCustomerPostcode());
        ret.setCustomerContactName(customerInfo.getCustomerContactName());
        return ret;
    }

    private static AfterSaleApplyParam.PickupInfo convertPickUp(AfterSalePickUpBO afterSalePickUp) {
        if (Objects.isNull(afterSalePickUp)) {
            return null;
        }
        AfterSaleApplyParam.PickupInfo pickupInfo = new AfterSaleApplyParam.PickupInfo();
        pickupInfo.setPickWay(PickupProductEnum.getPickupWareEnum(afterSalePickUp.getPickupType()));
        AddressInfoParam addressInfoParam = new AddressInfoParam();
        addressInfoParam.setProvinceCode(afterSalePickUp.getPickupProvinceCode());
        addressInfoParam.setCityCode(afterSalePickUp.getPickupCityCode());
        addressInfoParam.setCountyCode(afterSalePickUp.getPickupCountyCode());
        addressInfoParam.setTownCode(afterSalePickUp.getPickupTownCode());
        addressInfoParam.setAddressDetail(afterSalePickUp.getPickupAddress());
        pickupInfo.setAddressInfoParam(addressInfoParam);
        pickupInfo.setReservationStartTime(afterSalePickUp.getReservationStartTime().format(DateUtils.DEFAULT_DATE_TIME_FORMATTER));
        pickupInfo.setReservationEndTime(afterSalePickUp.getReservationEndTime().format(DateUtils.DEFAULT_DATE_TIME_FORMATTER));
        return pickupInfo;
    }

    private static AfterSaleApplyParam.AfterSaleSku convertAfterSaleSku(AfterSaleOutBO afterSale, AfterSaleItemOutBO item) {
        AfterSaleApplyParam.AfterSaleSku sku = new AfterSaleApplyParam.AfterSaleSku();
        sku.setChannelSkuCode(item.getChannelSkuCode());
        sku.setChannelSkuName(item.getSkuName());
        sku.setChannelGiftCode(item.getSkuCode());
        sku.setNum(item.getApplyNum());
        sku.setChannelPrice(item.getItemPlatformUnitPrice());
        sku.setApplyAmount(afterSale.getApplyRefundAmount());
        sku.setProductType(ProductTypeEnum.getProductTypeEnum(item.getProductType()));
        sku.setChannelReasonCode(item.getReasonCode());
        sku.setChannelReason(item.getReasonName());
        sku.setGoodsStatus(item.getGoodsStatus());
        sku.setChannelOrderSn(StringUtils.isEmpty(afterSale.getChannelOrderSn()) ?
                afterSale.getChannelTradeOrderSn() : afterSale.getChannelOrderSn());
        sku.setChannelPreOrderSn(StringUtils.isEmpty(afterSale.getChannelOrderParentSn()) ?
                sku.getChannelOrderSn() : afterSale.getChannelOrderParentSn());
        sku.setIsHasInvoice(Objects.equals(afterSale.getHasInvoice(), CommonConstants.ONE));
        sku.setChannelShippingFee(item.getFreightApplyRefundAmount());
        return sku;
    }

    private static AfterSaleApplyParam.ReturnInfo convertReturnShipment(AfterSaleReturnShipmentBO returnShipment) {
        if (Objects.isNull(returnShipment)) {
            return null;
        }
        AfterSaleApplyParam.ReturnInfo returnInfo = new AfterSaleApplyParam.ReturnInfo();
        returnInfo.setReturnWay(DeliveryWayEnum.getDeliveryWayEnum(returnShipment.getReturnType()));
        AddressInfoParam addressInfoParam = new AddressInfoParam();
        addressInfoParam.setProvinceCode(returnShipment.getReturnProvinceCode());
        addressInfoParam.setCityCode(returnShipment.getReturnCityCode());
        addressInfoParam.setCountyCode(returnShipment.getReturnCountyCode());
        addressInfoParam.setTownCode(returnShipment.getReturnTownCode());
        addressInfoParam.setAddressDetail(returnShipment.getReturnAddress());
        returnInfo.setAddressInfoParam(addressInfoParam);
        return returnInfo;
    }
}
